#include <types.h>
#include <arm.h>
#include <stdio.h>
//#include <mmu.h>
//#include <memlayout.h>
#include <console.h>
#include <trap.h>
#include <clock.h>
#include <assert.h>
#include <picirq.h>
#include <kdebug.h>

#define TICK_NUM 5

static void print_ticks() {
    cprintf("%d ticks\n",ticks);
#ifdef DEBUG_GRADE
    cprintf("End of Test.\n");
    panic("EOT: kernel seems ok.");
#endif
}

static const char *
trapname(int trapno) {
    static const char * const excnames[] = {
        "Reset",
        "Undefined instruction",
        "Software interrupt",
        "Prefetch abort",
        "Data abort",
        "Reserved",
        "Interrupt request",
        "Fast interrupt request"
    };
	
    if (trapno < sizeof(excnames)/sizeof(const char * const)) {
        return excnames[trapno];
    }
	
	if (trapno >= IRQ_OFFSET && trapno <= IRQ_OFFSET + IRQ_MAX_RANGE) {
		return "Hardware Interrupt";
    }
	
    return "(unknown trap)";
}

static const char * const modenames[] = {
	"User",
	"FIQ",
	"IRQ",
	"Supervisor",
	"","","",
	"Abort",
	"","","",
	"Undefined",
	"","","",
	"System"
};

/* trap_in_kernel - test if trap happened in kernel */
// TODO (or to understand)
bool
trap_in_kernel(struct trapframe *tf) {
    //return (tf->tf_cs == (uint16_t)KERNEL_CS);
	return 0;
}

void
print_trapframe(struct trapframe *tf) {
    cprintf("trapframe at %p\n", tf);
    print_regs(&tf->tf_regs);
	cprintf("  spsr 0x%08x %s\n", tf->tf_sr, modenames[tf->tf_sr & 0xF]);
    cprintf("  trap 0x%08x %s\n", tf->tf_trapno, trapname(tf->tf_trapno));
}

void
print_regs(struct pushregs *regs) {
	int i;
	for (i = 0; i < 11; i++) {
		cprintf("  r%02d  0x%08x\n", i, regs->reg_r[i]);
	}
	cprintf("  fp   0x%08x\n", regs->reg_r[11]);
	cprintf("  ip   0x%08x\n", regs->reg_r[12]);
    cprintf("  lr   0x%08x\n", regs->reg_r[13]);
}


/* trap_dispatch - dispatch based on what type of trap occurred */
static void
trap_dispatch(struct trapframe *tf) {
    char c;
	//cprintf("Trap [%03d %03d]\n", tf->tf_trapno, tf->tf_trapsubno);
	
    switch (tf->tf_trapno) {
	// IRQ Service Routine
	case IRQ_OFFSET + INT_TIMER4:
		ticks ++;
		if (ticks % TICK_NUM == 0) {
			print_ticks();
			//print_trapframe(tf);
		}
		break;
	case IRQ_OFFSET + INT_UART0:
		c = cons_getc();
		cprintf("serial [%03d] %c\n", c, c);
		break;
	// SWI Service Routine
    case T_SWITCH_TOK: // a random System call
		cprintf("Random system call\n") ;
		print_cur_status();
		//print_stackframe();
        break;
    default:
		print_trapframe(tf);
		panic("Trap in kernel.\n");
    }
}

/* *
 * trap - handles or dispatches an exception/interrupt. if and when trap() returns,
 * the code in kern/trap/trapentry.S will restore the state before the exception.
 * */
void
trap(struct trapframe * tf) {
    trap_dispatch(tf);
}
